//最简TSF输入程序

#include <windows.h>
#include <msctf.h>

#include <cassert>
#include <vector>

ITfThreadMgr *g_pThreadMgr=NULL;
ITfKeystrokeMgr *pKeyMgr=NULL;
ITfMessagePump *pMsgPump=NULL;
ITfDocumentMgr* pDocMgr=NULL;
ITfContext* pContext=NULL;
TfClientId tfClientId;
class CTSFEdit;
CTSFEdit* pEdit=NULL;
TfEditCookie EditCookie=TF_INVALID_EDIT_COOKIE;
ITextStoreACPSink* pTextStoreACPSink=NULL;
HWND hwnd;

//简单TSF文本编辑器
//通过打印消息来实现文本输出
class CTSFEdit : 
    public ITextStoreACP, //管理文本存储,ACP表示Applicaiton Character Position,即应用程序字符位置,字符位置从0开始
    public ITfContextOwnerCompositionSink  //管理文本输入
    // public ITfFunctionProvider //管理功能
{
protected:

    //存储文本
    std::vector<WCHAR> Text;

    //文本是否锁定
    BOOL bLocked;

public:

    void Init()
    {
        //创建文档管理器
        if (FAILED(g_pThreadMgr->CreateDocumentMgr(&pDocMgr)))
        {
            assert(false);
        }

        //创建上下文
        if (FAILED(pDocMgr->CreateContext(tfClientId, 0, (ITextStoreACP*)this, &pContext, &EditCookie)))
        {
            assert(false);
        }

        //推送上下文
        if (FAILED(pDocMgr->Push(pContext)))
        {
            assert(false);
        }



        

    }

    //ITextStoreACP
    STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj)
    {
        if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITextStoreACP))
        {
            *ppvObj = (ITextStoreACP*)this;
            return S_OK;
        }
        else if (IsEqualIID(riid, IID_ITfContextOwnerCompositionSink))
        {
            *ppvObj = (ITfContextOwnerCompositionSink*)this;
            return S_OK;
        }

        *ppvObj = NULL;
        return E_NOINTERFACE;
    }

    STDMETHODIMP_(ULONG) AddRef()
    {
        return 1;
    }

    STDMETHODIMP_(ULONG) Release()
    {
        return 1;
    }

    STDMETHODIMP AdviseSink(REFIID riid, IUnknown *punk, DWORD dwMask)
    {
        OutputDebugStringW(L"AdviseSink\n");


        if (IsEqualIID(riid, IID_ITextStoreACPSink))
        {
            punk->QueryInterface(IID_ITextStoreACPSink, (void**)&pTextStoreACPSink);
            return S_OK;
        }

        return E_NOTIMPL;
    }

    STDMETHODIMP UnadviseSink(IUnknown *punk)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP RequestLock(DWORD dwLockFlags, HRESULT *phrSession)
    {
        if (bLocked)
        {
            *phrSession = TS_E_SYNCHRONOUS;
            return TS_E_SYNCHRONOUS;
        }
        bLocked = TRUE;
        *phrSession = S_OK;
        return S_OK;
    }

    STDMETHODIMP GetStatus(TS_STATUS *pdcs)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP QueryInsert(LONG acpTestStart, LONG acpTestEnd, ULONG cch, LONG *pacpResultStart, LONG *pacpResultEnd)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP GetSelection(ULONG ulIndex, ULONG ulCount, TS_SELECTION_ACP *pSelection, ULONG *pcFetched)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP SetSelection(ULONG ulCount, const TS_SELECTION_ACP *pSelection)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP GetText(LONG acpStart, LONG acpEnd, WCHAR *pchPlain, ULONG cchPlainReq, ULONG *pcchPlainRet, TS_RUNINFO *prgRunInfo, ULONG ulRunInfoReq, ULONG *pulRunInfoRet, LONG *pacpNext)
    {
        OutputDebugStringW(L"GetText\n");

        return E_NOTIMPL;

    }

    STDMETHODIMP SetText(DWORD dwFlags, LONG acpStart, LONG acpEnd, const WCHAR *pchText, ULONG cch, TS_TEXTCHANGE *pChange)
    {
        OutputDebugStringW(L"SetText\n");

        //插入文本
        OutputDebugStringW(pchText);

        return S_OK;
    }

    STDMETHODIMP GetFormattedText(LONG acpStart, LONG acpEnd, IDataObject **ppDataObject)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP GetEmbedded(LONG acpPos, REFGUID rguidService, REFIID riid, IUnknown **ppunk)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP QueryInsertEmbedded(const GUID *pguidService, const FORMATETC *pFormatEtc, BOOL *pfInsertable)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP InsertTextAtSelection(DWORD dwFlags, const WCHAR *pwszText, ULONG cch, LONG *pacpStart, LONG *pacpEnd, TS_TEXTCHANGE *pChange)
    {
        //插入文本
        OutputDebugStringW(pwszText);
        return S_OK;
    }



    STDMETHODIMP InsertEmbeddedAtSelection(DWORD dwFlags, IDataObject *pDataObject, LONG *pacpStart, LONG *pacpEnd, TS_TEXTCHANGE *pChange)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP RequestSupportedAttrs(DWORD dwFlags, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs)
    {
        return E_NOTIMPL;
    }

    //insert embedded
    STDMETHODIMP InsertEmbedded(DWORD dwFlags, LONG acpStart, LONG acpEnd, IDataObject *pDataObject, TS_TEXTCHANGE *pChange)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP RequestAttrsAtPosition(LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP RequestAttrsTransitioningAtPosition(LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP FindNextAttrTransition(LONG acpStart, LONG acpHalt, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags, LONG *pacpNext, BOOL *pfFound, LONG *plFoundOffset)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP RetrieveRequestedAttrs(ULONG ulCount, TS_ATTRVAL *paAttrVals, ULONG *pcFetched)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP GetEndACP(LONG *pacp)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP GetActiveView(TsViewCookie *pvcView)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP GetACPFromPoint(TsViewCookie vcView, const POINT *pt, DWORD dwFlags, LONG *pacp)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP GetTextExt(TsViewCookie vcView, LONG acpStart, LONG acpEnd, RECT *prc, BOOL *pfClipped)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP GetScreenExt(TsViewCookie vcView, RECT *prc)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP GetWnd(TsViewCookie vcView, HWND *phwnd)
    {
        phwnd=&hwnd;
        return S_OK;
    }


    //ITfContextOwnerCompositionSink
    STDMETHODIMP OnStartComposition(ITfCompositionView *pComposition, BOOL *pfOk)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP OnUpdateComposition(ITfCompositionView *pComposition, ITfRange *pRangeNew)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP OnEndComposition(ITfCompositionView *pComposition)
    {
        //结束输入
        OutputDebugStringW(L"End Composition\n");
        return E_NOTIMPL;
    }

};


LRESULT CALLBACK _WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{

    switch (uMessage)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    case WM_CREATE:
        {
            //创建文本编辑器
            if (pEdit == NULL)
            {
                pEdit = new CTSFEdit();
                pEdit->Init();
            }
        }
        break;




    default:
        return DefWindowProc(hWnd, uMessage, wParam, lParam);
    }
    return 0;
}


int main()
{
    CoInitialize(NULL);



    //初始化TSF
    if (FAILED(CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void**)&g_pThreadMgr)))
    {
        assert(false);
    }

    //激活TSF
    g_pThreadMgr->Activate(&tfClientId);


    if (FAILED(g_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void**)&pKeyMgr)))
    {
        assert(false);
    }


    if (FAILED(g_pThreadMgr->QueryInterface(IID_ITfMessagePump, (void**)&pMsgPump)))
    {
        assert(false);
    }


    //注册窗口
    WNDCLASSW wc;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = _WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandle(NULL);
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = L"SimpleTSF";
    RegisterClassW(&wc);

    //创建窗口
     hwnd = CreateWindowW(L"SimpleTSF", L"SimpleTSF", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400, NULL, NULL, GetModuleHandle(NULL), NULL);
    ShowWindow(hwnd, SW_SHOWNORMAL);

    BOOL bLoop = TRUE;
    while (bLoop)
    {
        MSG msg;
        
        //获取消息，使用MsgPump->GetMessage 而不是GetMessage
        //因为MsgPump->GetMessage会执行消息前处理或消息后处理
        if(FAILED(pMsgPump->GetMessageW(&msg, NULL, 0, 0, &bLoop)))
        {
            break;
        }


        TranslateMessage(&msg);
        DispatchMessageW(&msg);

    }

    pKeyMgr->Release();
    pMsgPump->Release();
    g_pThreadMgr->Release();

    CoUninitialize();

    return 0;


}